From 92f11d34ed3d685bd4b2488d5d2c72326c32f5e1 Mon Sep 17 00:00:00 2001 From: "ach61@labyrinth.cl.cam.ac.uk" Date: Wed, 28 Jul 2004 16:42:58 +0000 Subject: [PATCH] bitkeeper revision 1.1098.2.1 (4107d792-8oUXilcPgo1Fl8qnr9ZsQ) large changes to pdb --- xen/arch/x86/pdb-linux.c | 30 +- xen/arch/x86/pdb-stub.c | 1120 ++++++++++++++++++++++++------------- xen/common/debug-linux.c | 119 ++-- xen/common/debug.c | 6 +- xen/include/asm-x86/pdb.h | 91 ++- 5 files changed, 871 insertions(+), 495 deletions(-) diff --git a/xen/arch/x86/pdb-linux.c b/xen/arch/x86/pdb-linux.c index fd0fc5ed78..965924f47a 100644 --- a/xen/arch/x86/pdb-linux.c +++ b/xen/arch/x86/pdb-linux.c @@ -10,8 +10,12 @@ * linux & i386 dependent code. bleech. */ +#include #include +extern unsigned char pdb_x86_bkpt; +extern int pdb_x86_bkpt_length; + /* offset to the first instruction in the linux system call code where we can safely set a breakpoint */ unsigned int pdb_linux_syscall_enter_bkpt_offset = 20; @@ -92,9 +96,25 @@ pdb_linux_syscall_exit_bkpt (struct pt_regs *regs, struct pdb_context *pdb_ctx) debugger when re-entering user space */ pdb_system_call_next_addr = *(unsigned long *)(regs->esp + pdb_linux_syscall_eip_offset); - pdb_linux_get_values (&pdb_system_call_leave_instr, 1, - pdb_system_call_next_addr, - pdb_ctx->process, pdb_ctx->ptbr); - pdb_linux_set_values ("cc", 1, pdb_system_call_next_addr, - pdb_ctx->process, pdb_ctx->ptbr); + + /* set a breakpoint when we exit */ + { + pdb_bwcpoint_p bwc = (pdb_bwcpoint_p) kmalloc(sizeof(pdb_bwcpoint_t)); + + bwc->address = pdb_system_call_next_addr; + bwc->length = 1; + bwc->type = PDB_BP_SOFTWARE; + bwc->user_type = PDB_BP_SOFTWARE; + bwc->original = pdb_system_call_leave_instr; + memcpy (&bwc->context, pdb_ctx, sizeof(pdb_context_t)); + + /* this is always in a process context */ + pdb_read_memory (pdb_system_call_next_addr, 1, + &pdb_system_call_leave_instr, + (pdb_context_p) pdb_ctx); + pdb_write_memory (pdb_system_call_next_addr, pdb_x86_bkpt_length, + &pdb_x86_bkpt, (pdb_context_p) pdb_ctx); + + pdb_bwc_list_add (bwc); + } } diff --git a/xen/arch/x86/pdb-stub.c b/xen/arch/x86/pdb-stub.c index 51cb898dc5..c4ebf74a3d 100644 --- a/xen/arch/x86/pdb-stub.c +++ b/xen/arch/x86/pdb-stub.c @@ -22,39 +22,57 @@ #include #include -#define PDB_DEBUG_TRACE -#ifdef PDB_DEBUG_TRACE -#define TRC(_x) _x -#else -#define TRC(_x) -#endif +int pdb_trace = 1; /* debugging the debugger */ #define DEBUG_EXCEPTION 0x01 #define BREAKPT_EXCEPTION 0x03 #define PDB_LIVE_EXCEPTION 0x58 #define KEYPRESS_EXCEPTION 0x88 -#define BUFMAX 400 - static const char hexchars[] = "0123456789abcdef"; -static int remote_debug; - #define PDB_BUFMAX 1024 static char pdb_in_buffer[PDB_BUFMAX]; static char pdb_out_buffer[PDB_BUFMAX]; -static char pdb_buffer[PDB_BUFMAX]; static int pdb_in_buffer_ptr; static unsigned char pdb_in_checksum; static unsigned char pdb_xmit_checksum; -struct pdb_context pdb_ctx; +void pdb_put_packet (unsigned char *buffer, int ack); + +pdb_context_t pdb_ctx; int pdb_continue_thread = 0; int pdb_general_thread = 0; -void pdb_put_packet (unsigned char *buffer, int ack); -void pdb_bkpt_check (u_char *buffer, int length, - unsigned long cr3, unsigned long addr); + +enum pdb_bwc_page_action +{ + PDB_BWC_PAGE_ACCESS_SET, + PDB_BWC_PAGE_ACCESS_CLEAR, + PDB_BWC_PAGE_WRITE_SET, + PDB_BWC_PAGE_WRITE_CLEAR, + PDB_BWC_PAGE_READ_SET, + PDB_BWC_PAGE_READ_CLEAR, +}; +static char *pdb_bwc_page_action_s[] = + { "ac set", "ac clr", "wr set", "wr clr", "rd set", "rd cler" }; +int pdb_bwc_page (int action, unsigned long addr, int length, + pdb_context_p ctx, int offset, void *s); + +enum pdb_visit_page_action +{ + PDB_VISIT_PAGE_XEN_READ, + PDB_VISIT_PAGE_XEN_WRITE, + PDB_VISIT_PAGE_DOMAIN_READ, + PDB_VISIT_PAGE_DOMAIN_WRITE, + PDB_VISIT_PAGE_PROCESS_READ, + PDB_VISIT_PAGE_PROCESS_WRITE, +}; +static char *pdb_visit_page_action_s[] = + { "xen rd", "xen wr", "dom rd", "dom wr", "proc rd", "proc wr" }; + +int pdb_visit_page (int action, unsigned long addr, int length, + pdb_context_p ctx, int offset, void *s); int pdb_initialized = 0; int pdb_page_fault_possible = 0; @@ -69,6 +87,12 @@ unsigned char pdb_system_call_leave_instr = 0; /* original next instr */ unsigned long pdb_system_call_next_addr = 0; /* instr after int 0x80 */ unsigned long pdb_system_call_eflags_addr = 0; /* saved eflags on stack */ +unsigned char pdb_x86_bkpt = 0xcc; +unsigned int pdb_x86_bkpt_length = 1; + +/***********************************************************************/ +/***********************************************************************/ + static inline void pdb_put_char(unsigned char c) { serial_putc(pdb_serhnd, c); @@ -79,18 +103,136 @@ static inline unsigned char pdb_get_char(void) return serial_getc(pdb_serhnd); } +/***********************************************************************/ +/***********************************************************************/ + +/* + * Prototype for function to process each page. This function is called + * once per page. + * + * action : function specific + * address : first byte of this page + * length : number of bytes to process on this page + * offset : number of bytes processed so far. can be used as + * an index into data. + * data : function specific. + */ + +typedef int (pdb_invoke_ftype) (int action, unsigned long address, int length, + pdb_context_p ctx, int offset, void *data); + +typedef struct pdb_invoke_args +{ + pdb_context_p context; + unsigned long address; + int length; + int action; + void *data; +} pdb_invoke_args_t, * pdb_invoke_args_p; + + +/* + * call a particular function once per page given an address & length + */ + int -get_char (char *addr) +pdb_invoke(pdb_invoke_ftype *function, pdb_invoke_args_p args) +{ + int remaining; + int bytes = 0; + int length = args->length; + unsigned long address = args->address; + + while ((remaining = (address + length - 1) - (address | (PAGE_SIZE - 1))) > 0) + { + bytes += (function)(args->action, address, length - remaining, + args->context, address - args->address, args->data); + length = remaining; + address = (address | (PAGE_SIZE - 1)) + 1; + } + bytes += (function)(args->action, address, length, + args->context, address - args->address, args->data); + return bytes; +} + + +/***********************************************************************/ +/***********************************************************************/ + +/* BWC List Support: breakpoints, watchpoints, and catchpoints */ + +char *pdb_bwcpoint_type_s[] = /* enum pdb_bwcpoint_type */ + { "BP_SOFTWARE", "BP_HARDWARE", "WP_WRITE", "WP_READ", "WP_ACCESS" }; + +int pdb_set_watchpoint (pdb_bwcpoint_p bwc); +int pdb_clear_watchpoint (pdb_bwcpoint_p bwc); + +struct list_head pdb_bwc_list = LIST_HEAD_INIT(pdb_bwc_list); + +void +pdb_bwc_list_add (pdb_bwcpoint_p bwc) +{ + list_add_tail(&bwc->list, &pdb_bwc_list); +} + +void +pdb_bwc_list_remove (pdb_bwcpoint_p bwc) +{ + list_del(&bwc->list); +} + +pdb_bwcpoint_p +pdb_bwc_list_search (unsigned long address, int length, pdb_context_p ctx) +{ + struct list_head *ptr; + + list_for_each (ptr, &pdb_bwc_list) + { + pdb_bwcpoint_p bwc = list_entry(ptr, pdb_bwcpoint_t, list); + + if (bwc->address == address && + bwc->length == length) + { + return bwc; + } + } + return (pdb_bwcpoint_p) 0; +} + +pdb_bwcpoint_p +pdb_bwcpoint_search (unsigned long cr3, unsigned long address) { - return *addr; + pdb_context_t ctx; + + ctx.ptbr = cr3; + return pdb_bwc_list_search (address, pdb_x86_bkpt_length, &ctx); } void -set_char (char *addr, int val) +pdb_bwc_print (pdb_bwcpoint_p bwc) { - *addr = val; + printk ("address: 0x%08lx, length: 0x%02x, type: 0x%x %s", + bwc->address, bwc->length, + bwc->type, pdb_bwcpoint_type_s[bwc->type]); } +void +pdb_bwc_print_list () +{ + struct list_head *ptr; + int counter = 0; + + list_for_each (ptr, &pdb_bwc_list) + { + pdb_bwcpoint_p bwc = list_entry(ptr, pdb_bwcpoint_t, list); + printk (" [%02d] ", counter); pdb_bwc_print(bwc); printk ("\n"); + counter++; + } +} + +/***********************************************************************/ +/***********************************************************************/ + void pdb_process_query (char *ptr) { @@ -100,144 +242,230 @@ pdb_process_query (char *ptr) } else if (strcmp(ptr, "fThreadInfo") == 0) { -#ifdef PDB_PAST - struct domain *p; - u_long flags; -#endif /* PDB_PAST */ + int buf_idx = 0; + pdb_out_buffer[buf_idx++] = 'l'; + pdb_out_buffer[buf_idx++] = 0; + } + else if (strcmp(ptr, "sThreadInfo") == 0) + { int buf_idx = 0; pdb_out_buffer[buf_idx++] = 'l'; pdb_out_buffer[buf_idx++] = 0; + } + else if (strncmp(ptr, "ThreadExtraInfo,", 16) == 0) + { + int thread = 0; + char *message = "foobar ?"; -#ifdef PDB_PAST - switch (pdb_level) - { - case PDB_LVL_XEN: /* return a list of domains */ + ptr += 16; + if (hexToInt (&ptr, &thread)) { - int count = 0; + mem2hex (message, pdb_out_buffer, strlen(message) + 1); + } + +#ifdef PDB_FUTURE + { + char string[task_struct_comm_length]; + + string[0] = 0; + pdb_linux_process_details (cr3, pid, string); + printk (" (%s)", string); + } +#endif /* PDB_FUTURE*/ + } + else if (strcmp(ptr, "Offsets") == 0) + { + /* empty string */ + } + else if (strncmp(ptr, "Symbol", 6) == 0) + { + strcpy (pdb_out_buffer, "OK"); + } + else + { + printk("pdb: error, unknown query [%s]\n", ptr); + } +} - read_lock_irqsave (&tasklist_lock, flags); +void +pdb_process_z (int onoff, char *ptr) +{ + int type = *(ptr++) - '0'; + int length; + unsigned long addr; + char *error = "E01"; /* syntax error */ + + /* try to read ',addr,length' */ + if ( *(ptr++) == ',' + && hexToInt(&ptr, (int *)&addr) + && *(ptr++) == ',' + && hexToInt(&ptr, &length)) + { + error = "OK"; - pdb_out_buffer[buf_idx++] = 'm'; - for_each_domain ( p ) + switch (type) + { + case PDB_BP_SOFTWARE: + case PDB_BP_HARDWARE: + { + if (onoff == 1) { - domid_t domain = p->domain + PDB_ID_OFFSET; + pdb_bwcpoint_p bwc = (pdb_bwcpoint_p) kmalloc(sizeof(pdb_bwcpoint_t)); + + bwc->address = addr; + bwc->length = pdb_x86_bkpt_length; + bwc->type = PDB_BP_SOFTWARE; + bwc->user_type = type; + memcpy (&bwc->context, &pdb_ctx, sizeof(pdb_context_t)); - if (count > 0) + if (length != pdb_x86_bkpt_length) { - pdb_out_buffer[buf_idx++] = ','; + printk("pdb warning: x86 bkpt length should be 1\n"); } - if (domain > 15) + + pdb_set_breakpoint (bwc); + } + else + { + pdb_clear_breakpoint (addr, pdb_x86_bkpt_length, &pdb_ctx); + pdb_bwcpoint_p bwc = pdb_bwc_list_search (addr, 1, &pdb_ctx); + + if (bwc == 0) { - pdb_out_buffer[buf_idx++] = hexchars[domain >> 4]; + error = "E03"; /* breakpoint not found */ + break; } - pdb_out_buffer[buf_idx++] = hexchars[domain % 16]; - count++; - } - pdb_out_buffer[buf_idx++] = 0; - read_unlock_irqrestore(&tasklist_lock, flags); + pdb_write_memory (addr, 1, &bwc->original, &pdb_ctx); + + pdb_bwc_list_remove (bwc); + } break; } - case PDB_LVL_GUESTOS: /* return a list of processes */ + case PDB_WP_WRITE: + case PDB_WP_READ: + case PDB_WP_ACCESS: { - int foobar[20]; - int loop, total; + if (onoff == 1) + { + pdb_bwcpoint_p bwc = (pdb_bwcpoint_p) kmalloc(sizeof(pdb_bwcpoint_t)); + + bwc->address = addr; + bwc->length = length; + bwc->type = type; + bwc->user_type = type; + memcpy (&bwc->context, &pdb_ctx, sizeof(pdb_context_t)); - /* this cr3 is wrong! */ - total = pdb_linux_process_list(pdb_ctx[pdb_level].info_cr3, - foobar, 20); + pdb_set_watchpoint (bwc); - pdb_out_buffer[buf_idx++] = 'm'; - pdb_out_buffer[buf_idx++] = '1'; /* 1 is to go back */ - for (loop = 0; loop < total; loop++) + pdb_bwc_list_add (bwc); + } + else { - int pid = foobar[loop] + PDB_ID_OFFSET; + pdb_bwcpoint_p bwc = pdb_bwc_list_search (addr, 1, &pdb_ctx); - pdb_out_buffer[buf_idx++] = ','; - if (pid > 15) + if (bwc == 0) { - pdb_out_buffer[buf_idx++] = hexchars[pid >> 4]; + error = "E03"; /* watchpoint not found */ + break; } - pdb_out_buffer[buf_idx++] = hexchars[pid % 16]; + + pdb_clear_watchpoint (bwc); + + pdb_bwc_list_remove (bwc); } - pdb_out_buffer[buf_idx++] = 0; break; } - case PDB_LVL_PROCESS: /* hmmm... */ + default: { - pdb_out_buffer[buf_idx++] = 'm'; - pdb_out_buffer[buf_idx++] = '1'; /* 1 is to go back */ + printk ("pdb error: unknown Z command [%c]\n", type); + error = "E02"; /* syntax error */ break; } - default: - break; } -#endif /* PDB_PAST */ - } - else if (strcmp(ptr, "sThreadInfo") == 0) - { - int buf_idx = 0; - pdb_out_buffer[buf_idx++] = 'l'; - pdb_out_buffer[buf_idx++] = 0; - } - else if (strncmp(ptr, "ThreadExtraInfo,", 16) == 0) + if (error) /* return value, including okay */ { - int thread = 0; - char *message = "foobar ?"; + strcpy (pdb_out_buffer, error); + } +} - ptr += 16; - if (hexToInt (&ptr, &thread)) +void +pdb_process_pdb (char *ptr) +{ + unsigned long arg1, arg2; + char *error = "E01"; /* syntax error */ + char command = *(ptr++); + + switch (command) + { + case 'c': /* set pdb context */ + case 'C': + { + /* try to read two hex arguments ':arg1,arg2 */ + if ( *(ptr++) == ':' + && hexToInt(&ptr, (int *)&arg1) + && *(ptr++) == ',' + && hexToInt(&ptr, (int *)&arg2)) { - mem2hex (message, pdb_out_buffer, strlen(message) + 1); + printk ("pdb: set context: domain:0x%lx process:0x%lx\n", + arg1, arg2); + error = "OK"; } -#ifdef PDB_PAST - int thread = 0; - char message[16]; - struct domain *p; - - p = find_domain_by_id(pdb_ctx[pdb_level].info); - strncpy (message, p->name, 16); - put_domain(p); - - ptr += 16; - if (hexToInt (&ptr, &thread)) + pdb_ctx.domain = arg1; + pdb_ctx.process = arg2; + pdb_ctx.valid = 1; + break; + } + case 't': /* enable pdb tracing */ + case 'T': + { + /* read the trace level */ + if ( *(ptr++) == ':' + && hexToInt(&ptr, (int *)&pdb_trace)) { - mem2hex ((char *)message, pdb_out_buffer, strlen(message) + 1); + printk ("pdb: set trace level: 0x%x\n", pdb_trace); + error = "OK"; } -#endif /* PDB_PAST */ - -#ifdef PDB_FUTURE - { - char string[task_struct_comm_length]; - - string[0] = 0; - pdb_linux_process_details (cr3, pid, string); - printk (" (%s)", string); - } -#endif /* PDB_FUTURE*/ - + break; } - else if (strcmp(ptr, "Offsets") == 0) + case 'd': + case 'D': /* dump pdb state */ { - /* empty string */ + printk ("----------\n"); + printk ("pdb trace : %2d 0x%02x\n", pdb_trace, pdb_trace); + printk ("pdb ctx domain : %4d 0x%04x\n", + pdb_ctx.domain, pdb_ctx.domain); + printk (" process : %4d 0x%04x\n", + pdb_ctx.process, pdb_ctx.process); + printk (" sys call: %4d 0x%04x\n", + pdb_ctx.system_call, pdb_ctx.system_call); + printk ("bwc list:\n"); + pdb_bwc_print_list (); + printk ("----------\n"); + error = "OK"; + break; } - else if (strncmp(ptr, "Symbol", 6) == 0) + default: { - strcpy (pdb_out_buffer, "OK"); + printk ("pdb error: unknown pdb dot command [%c]\n", command); + error = "E02"; /* syntax error */ + break; } - else + } + + if (error) /* return value, including okay */ { - printk("pdb: error, unknown query [%s]\n", ptr); + strcpy (pdb_out_buffer, error); } } void -pdb_x86_to_gdb_regs (char *buffer, struct pt_regs *regs) +pdb_read_regs (char *buffer, struct pt_regs *regs) { int idx = 0; @@ -276,7 +504,7 @@ pdb_x86_to_gdb_regs (char *buffer, struct pt_regs *regs) /* at this point we allow any register to be changed, caveat emptor */ void -pdb_gdb_to_x86_regs (struct pt_regs *regs, char *buffer) +pdb_write_regs (struct pt_regs *regs, char *buffer) { hex2mem(buffer, (char *)®s->eax, sizeof(regs->eax)); buffer += sizeof(regs->eax) * 2; @@ -320,7 +548,7 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3, int ack = 1; /* wait for ack in pdb_put_packet */ int go = 0; - TRC(printf("pdb: [%s]\n", ptr)); + PDBTRC(1,printk("pdb: [%s]\n", ptr)); pdb_out_buffer[0] = 0; @@ -386,8 +614,8 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3, } pdb_ctx.valid = 0; - TRC(printk ("pdb change context (dom:%d, proc:%d) now 0x%lx\n", - pdb_ctx.domain, pdb_ctx.process, pdb_ctx.ptbr)); + PDBTRC(1,printk ("pdb change context (dom:%d, proc:%d) now 0x%lx\n", + pdb_ctx.domain, pdb_ctx.process, pdb_ctx.ptbr)); } switch (*ptr++) @@ -404,16 +632,13 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3, if ( pdb_system_call_eflags_addr != 0 ) { unsigned long eflags; - char eflags_buf[sizeof(eflags)*2]; /* STUPID STUPID STUPID */ - pdb_linux_get_values((u_char*)&eflags, sizeof(eflags), - pdb_system_call_eflags_addr, - pdb_ctx.process, pdb_ctx.ptbr); + /* this is always in a process context */ + pdb_read_memory (pdb_system_call_eflags_addr, sizeof(eflags), + (u_char *)&eflags, &pdb_ctx); eflags |= X86_EFLAGS_TF; - mem2hex ((u_char *)&eflags, eflags_buf, sizeof(eflags)); - pdb_linux_set_values(eflags_buf, sizeof(eflags), - pdb_system_call_eflags_addr, - pdb_ctx.process, pdb_ctx.ptbr); + pdb_write_memory (pdb_system_call_eflags_addr, sizeof(eflags), + (u_char *)&eflags, &pdb_ctx); } regs->eflags |= X86_EFLAGS_TF; @@ -427,16 +652,13 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3, if ( pdb_system_call_eflags_addr != 0 ) { unsigned long eflags; - char eflags_buf[sizeof(eflags)*2]; /* STUPID STUPID STUPID */ - pdb_linux_get_values((u_char*)&eflags, sizeof(eflags), - pdb_system_call_eflags_addr, - pdb_ctx.process, pdb_ctx.ptbr); + /* this is always in a process context */ + pdb_read_memory (pdb_system_call_eflags_addr, sizeof(eflags), + (u_char *)&eflags, &pdb_ctx); eflags &= ~X86_EFLAGS_TF; - mem2hex ((u_char *)&eflags, eflags_buf, sizeof(eflags)); - pdb_linux_set_values(eflags_buf, sizeof(eflags), - pdb_system_call_eflags_addr, - pdb_ctx.process, pdb_ctx.ptbr); + pdb_write_memory (pdb_system_call_eflags_addr, sizeof(eflags), + (u_char *)&eflags, &pdb_ctx); } regs->eflags &= ~X86_EFLAGS_TF; @@ -444,19 +666,18 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3, /* not reached */ } case 'd': - remote_debug = !(remote_debug); /* toggle debug flag */ break; case 'D': /* detach */ - return go; - /* not reached */ + go = 1; + break; case 'g': /* return the value of the CPU registers */ { - pdb_x86_to_gdb_regs (pdb_out_buffer, regs); + pdb_read_regs (pdb_out_buffer, regs); break; } case 'G': /* set the value of the CPU registers - return OK */ { - pdb_gdb_to_x86_regs (regs, ptr); + pdb_write_regs (regs, ptr); break; } case 'H': @@ -509,24 +730,20 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3, { ptr = 0; - pdb_page_fault_possible = 1; + pdb_page_fault_possible = 2; pdb_page_fault = 0; - if (addr >= PAGE_OFFSET) - { - mem2hex ((char *) addr, pdb_out_buffer, length); - } - else if (pdb_ctx.process != -1) - { - pdb_linux_get_values(pdb_buffer, length, addr, - pdb_ctx.process, pdb_ctx.ptbr); - mem2hex (pdb_buffer, pdb_out_buffer, length); - } - else - { - pdb_get_values (pdb_buffer, length, - pdb_ctx.ptbr, addr); - mem2hex (pdb_buffer, pdb_out_buffer, length); - } + + { + u_char *buffer = (u_char *) kmalloc (length); + if (!buffer) + { + printk ("pdb error: kmalloc failure\n"); + break; + } + pdb_read_memory (addr, length, buffer, &pdb_ctx); + mem2hex (buffer, pdb_out_buffer, length); + kfree(buffer); + } pdb_page_fault_possible = 0; if (pdb_page_fault) @@ -546,44 +763,39 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3, case 'M': { /* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */ - if (hexToInt (&ptr, (int *)&addr)) - if (*(ptr++) == ',') - if (hexToInt (&ptr, &length)) - if (*(ptr++) == ':') - { - - pdb_page_fault_possible = 1; - pdb_page_fault = 0; - if (addr >= PAGE_OFFSET) - { - hex2mem (ptr, (char *)addr, length); - pdb_bkpt_check(ptr, length, pdb_ctx.ptbr, addr); - } - else if (pdb_ctx.process != -1) - { - pdb_linux_set_values(ptr, length, addr, - pdb_ctx.process, - pdb_ctx.ptbr); - pdb_bkpt_check(ptr, length, pdb_ctx.ptbr, addr); - } - else - { - pdb_set_values (ptr, length, - pdb_ctx.ptbr, addr); - pdb_bkpt_check(ptr, length, pdb_ctx.ptbr, addr); - } - pdb_page_fault_possible = 0; - if (pdb_page_fault) - { - strcpy (pdb_out_buffer, "E03"); - } - else - { - strcpy (pdb_out_buffer, "OK"); - } - - ptr = 0; - } + if ( hexToInt (&ptr, (int *)&addr) + && *(ptr++) == ',' + && hexToInt (&ptr, &length) + && *(ptr++) == ':') + { + pdb_page_fault_possible = 3; + pdb_page_fault = 0; + + { + u_char *buffer = (u_char *) kmalloc (length); + if (!buffer) + { + printk ("pdb error: kmalloc failure\n"); + break; + } + hex2mem (ptr, buffer, length); + pdb_write_memory (addr, length, buffer, &pdb_ctx); + kfree(buffer); + } + + pdb_page_fault_possible = 0; + if (pdb_page_fault) + { + strcpy (pdb_out_buffer, "E03"); + } + else + { + strcpy (pdb_out_buffer, "OK"); + } + + ptr = 0; + } + if (ptr) { strcpy (pdb_out_buffer, "E02"); @@ -597,59 +809,29 @@ pdb_process_command (char *ptr, struct pt_regs *regs, unsigned long cr3, if (hexToInt (&ptr, &id)) { strcpy (pdb_out_buffer, "E00"); - -#ifdef PDB_PAST - - switch (pdb_level) /* previous level */ - { - case PDB_LVL_XEN: - { - struct domain *p; - id -= PDB_ID_OFFSET; - if ( (p = find_domain_by_id(id)) == NULL) - strcpy (pdb_out_buffer, "E00"); - else - strcpy (pdb_out_buffer, "OK"); - put_domain(p); - - pdb_level = PDB_LVL_GUESTOS; - pdb_ctx[pdb_level].ctrl = id; - pdb_ctx[pdb_level].info = id; - break; - } - case PDB_LVL_GUESTOS: - { - if (pdb_level == -1) - { - pdb_level = PDB_LVL_XEN; - } - else - { - pdb_level = PDB_LVL_PROCESS; - pdb_ctx[pdb_level].ctrl = id; - pdb_ctx[pdb_level].info = id; - } - break; - } - case PDB_LVL_PROCESS: - { - if (pdb_level == -1) - { - pdb_level = PDB_LVL_GUESTOS; - } - break; - } - default: - { - printk ("pdb internal error: invalid level [%d]\n", - pdb_level); - } - } - -#endif /* PDB_PAST */ } break; } + case 'Z': /* set */ + { + pdb_process_z (1, ptr); + break; + } + case 'z': /* clear */ + { + pdb_process_z (0, ptr); + break; + } + case '.': /* pdb specific extensions */ + { + pdb_process_pdb (ptr); + break; + } + default: + { + PDBTRC(1,printk ("pdb warning: ignoring unknown command.\n")); + break; + } } exit: @@ -712,8 +894,8 @@ int pdb_serial_input(u_char c, struct pt_regs *regs) if (pdb_in_checksum != pdb_xmit_checksum) { pdb_put_char('-'); /* checksum failure */ - printk ("checksum failure [%s.%02x.%02x]\n", pdb_in_buffer, - pdb_in_checksum, pdb_xmit_checksum); + printk ("pdb error: checksum failure [%s.%02x.%02x]\n", + pdb_in_buffer, pdb_in_checksum, pdb_xmit_checksum); } else { @@ -738,6 +920,9 @@ int pdb_serial_input(u_char c, struct pt_regs *regs) return out; } +/***********************************************************************/ +/***********************************************************************/ + int hex(char ch) { if ((ch >= 'a') && (ch <= 'f')) return (ch-'a'+10); @@ -759,7 +944,8 @@ mem2hex (mem, buf, count) for (i = 0; i < count; i++) { - ch = get_char (mem++); + ch = *mem; + mem ++; *buf++ = hexchars[ch >> 4]; *buf++ = hexchars[ch % 16]; } @@ -782,7 +968,8 @@ hex2mem (buf, mem, count) { ch = hex (*buf++) << 4; ch = ch + hex (*buf++); - set_char (mem++, ch); + *mem = ch; + mem++; } return (mem); } @@ -826,150 +1013,153 @@ hexToInt (char **ptr, int *intValue) /***********************************************************************/ /***********************************************************************/ +/* READ / WRITE MEMORY */ +int pdb_change_page (u_char *buffer, int length, + unsigned long cr3, unsigned long addr, int rw); +int pdb_visit_memory (unsigned long addr, int length, unsigned char *data, + pdb_context_p ctx, pdb_generic_action action); -/* - * Add a breakpoint to the list of known breakpoints. - * For now there should only be two or three breakpoints so - * we use a simple linked list. In the future, maybe a red-black tree? - */ -struct pdb_breakpoint breakpoints; - -void pdb_bkpt_add (unsigned long cr3, unsigned long address) +int +pdb_read_memory (unsigned long addr, int length, unsigned char *data, + pdb_context_p ctx) { - struct pdb_breakpoint *bkpt = kmalloc(sizeof(*bkpt)); - bkpt->cr3 = cr3; - bkpt->address = address; - list_add(&bkpt->list, &breakpoints.list); + return pdb_visit_memory (addr, length, data, ctx, __PDB_GET); } -/* - * Check to see of the breakpoint is in the list of known breakpoints - * Return 1 if it has been set, NULL otherwise. - */ -struct pdb_breakpoint* pdb_bkpt_search (unsigned long cr3, - unsigned long address) +int +pdb_write_memory (unsigned long addr, int length, unsigned char *data, + pdb_context_p ctx) { - struct list_head *list_entry; - struct pdb_breakpoint *bkpt; - - list_for_each(list_entry, &breakpoints.list) - { - bkpt = list_entry(list_entry, struct pdb_breakpoint, list); - if ( bkpt->cr3 == cr3 && bkpt->address == address ) - return bkpt; - } - - return NULL; + return pdb_visit_memory (addr, length, data, ctx, __PDB_SET); } /* - * Remove a breakpoint to the list of known breakpoints. - * Return 1 if the element was not found, otherwise 0. + * either read or write a block of memory */ -int pdb_bkpt_remove (unsigned long cr3, unsigned long address) + +int +pdb_visit_memory (unsigned long addr, int length, unsigned char *data, + pdb_context_p ctx, pdb_generic_action action) { - struct list_head *list_entry; - struct pdb_breakpoint *bkpt; + int return_value; + pdb_invoke_args_t args; - list_for_each(list_entry, &breakpoints.list) - { - bkpt = list_entry(list_entry, struct pdb_breakpoint, list); - if ( bkpt->cr3 == cr3 && bkpt->address == address ) - { - list_del(&bkpt->list); - kfree(bkpt); - return 0; - } - } + pdb_page_fault_possible = 4; + pdb_page_fault = 0; - return 1; -} + args.context = ctx; + args.address = addr; + args.length = length; + args.data = data; -/* - * Check to see if a memory write is really gdb setting a breakpoint - */ -void pdb_bkpt_check (u_char *buffer, int length, - unsigned long cr3, unsigned long addr) -{ - if (length == 1 && buffer[0] == 'c' && buffer[1] == 'c') + if (addr >= PAGE_OFFSET) /* Xen */ { - /* inserting a new breakpoint */ - pdb_bkpt_add(cr3, addr); - TRC(printk("pdb breakpoint detected at 0x%lx:0x%lx\n", cr3, addr)); + args.action = (action == __PDB_GET) ? PDB_VISIT_PAGE_XEN_READ + : PDB_VISIT_PAGE_XEN_WRITE; } - else if ( pdb_bkpt_remove(cr3, addr) == 0 ) + else if (pdb_ctx.process != -1) /* Process */ { - /* removing a breakpoint */ - TRC(printk("pdb breakpoint cleared at 0x%lx:0x%lx\n", cr3, addr)); + args.action = (action == __PDB_GET) ? PDB_VISIT_PAGE_PROCESS_READ + : PDB_VISIT_PAGE_PROCESS_WRITE; + } + else /* Domain */ + { + args.action = (action == __PDB_GET) ? PDB_VISIT_PAGE_DOMAIN_READ + : PDB_VISIT_PAGE_DOMAIN_WRITE; } -} - -/***********************************************************************/ -int pdb_change_values(u_char *buffer, int length, - unsigned long cr3, unsigned long addr, int rw); -int pdb_change_values_one_page(u_char *buffer, int length, - unsigned long cr3, unsigned long addr, int rw); + return_value = pdb_invoke (pdb_visit_page, &args); -#define __PDB_GET_VAL 1 -#define __PDB_SET_VAL 2 + pdb_page_fault_possible = 0; + if (pdb_page_fault || return_value < 0) + { + strcpy (pdb_out_buffer, "E03"); + } + + return return_value; +} /* - * Set memory in a domain's address space - * Set "length" bytes at "address" from "domain" to the values in "buffer". - * Return the number of bytes set, 0 if there was a problem. + * either read or write a single page */ -int pdb_set_values(u_char *buffer, int length, - unsigned long cr3, unsigned long addr) +int +pdb_visit_page (int action, unsigned long addr, int length, + pdb_context_p ctx, int offset, void *data) { - int count = pdb_change_values(buffer, length, cr3, addr, __PDB_SET_VAL); - return count; + int rval; + + PDBTRC(2,printk ("visit: %s [0x%08lx:%x] 0x%x (0x%p)\n", + pdb_visit_page_action_s[action], + addr, length, offset, data)); + + switch (action) + { + case PDB_VISIT_PAGE_XEN_READ : + { + memcpy ((void *) data, (void *) addr, length); + rval = length;; + break; + } + case PDB_VISIT_PAGE_XEN_WRITE : + { + memcpy ((void *) addr, (void *) data, length); + rval = length; + break; + } + case PDB_VISIT_PAGE_DOMAIN_READ : + case PDB_VISIT_PAGE_DOMAIN_WRITE : + { + rval = pdb_change_page (data, length, ctx->ptbr, addr, + (action == PDB_VISIT_PAGE_DOMAIN_READ) ? __PDB_GET : __PDB_SET); + break; + } + case PDB_VISIT_PAGE_PROCESS_READ : + case PDB_VISIT_PAGE_PROCESS_WRITE : + { + u_char pdb_linux_visit_page(int pid, unsigned long cr3, unsigned long addr, int length, unsigned char *buffer, int action); + + rval = pdb_linux_visit_page (ctx->process, ctx->ptbr, addr, length, data, + (action == PDB_VISIT_PAGE_PROCESS_READ) ? __PDB_GET : __PDB_SET); + break; + } + default : + { + printk ("pdb error: unknown visit page action [%d]\n", action); + break; + } + } + + return 1; } -/* - * Read memory from a domain's address space. - * Fetch "length" bytes at "address" from "domain" into "buffer". - * Return the number of bytes read, 0 if there was a problem. - */ +/**************************************/ +/**************************************/ -int pdb_get_values(u_char *buffer, int length, - unsigned long cr3, unsigned long addr) +int +pdb_read_page(u_char *buffer, int length, + unsigned long cr3, unsigned long addr) { - return pdb_change_values(buffer, length, cr3, addr, __PDB_GET_VAL); + return pdb_change_page(buffer, length, cr3, addr, __PDB_GET); } -/* - * Read or write memory in an address space - */ -int pdb_change_values(u_char *buffer, int length, - unsigned long cr3, unsigned long addr, int rw) +int +pdb_write_page(u_char *buffer, int length, + unsigned long cr3, unsigned long addr) { - int remaining; /* number of bytes to touch past this page */ - int bytes = 0; - - while ( (remaining = (addr + length - 1) - (addr | (PAGE_SIZE - 1))) > 0) - { - bytes += pdb_change_values_one_page(buffer, length - remaining, - cr3, addr, rw); - buffer = buffer + (2 * (length - remaining)); - length = remaining; - addr = (addr | (PAGE_SIZE - 1)) + 1; - } - - bytes += pdb_change_values_one_page(buffer, length, cr3, addr, rw); - return bytes; + return pdb_change_page(buffer, length, cr3, addr, __PDB_SET); } /* - * Change memory in a process' address space in one page + * Change memory in one page of an address space. * Read or write "length" bytes at "address" into/from "buffer" * from the virtual address space referenced by "cr3". * Return the number of bytes read, 0 if there was a problem. */ -int pdb_change_values_one_page(u_char *buffer, int length, - unsigned long cr3, unsigned long addr, int rw) +int +pdb_change_page(u_char *buffer, int length, + unsigned long cr3, unsigned long addr, int rw) { l2_pgentry_t* l2_table = NULL; /* page directory */ l1_pgentry_t* l1_table = NULL; /* page table */ @@ -980,10 +1170,11 @@ int pdb_change_values_one_page(u_char *buffer, int length, l2_table += l2_table_offset(addr); if (!(l2_pgentry_val(*l2_table) & _PAGE_PRESENT)) { - if (pdb_page_fault_possible == 1) + if (pdb_page_fault_possible) { pdb_page_fault = 1; - TRC(printk("pdb: L2 error (0x%lx)\n", addr)); + PDBTRC2(1,printk("pdb: expected L2 error %d (0x%lx)\n", + pdb_page_fault_possible, addr)); } else { @@ -1021,7 +1212,7 @@ int pdb_change_values_one_page(u_char *buffer, int length, if (pdb_page_fault_possible == 1) { pdb_page_fault = 1; - TRC(printk ("pdb: L1 error (0x%lx)\n", addr)); + PDBTRC(1,printk ("pdb: L1 error (0x%lx)\n", addr)); } else { @@ -1038,18 +1229,25 @@ int pdb_change_values_one_page(u_char *buffer, int length, switch (rw) { - case __PDB_GET_VAL: /* read */ + case __PDB_GET: /* read */ + { memcpy (buffer, page, length); bytes = length; + break; - case __PDB_SET_VAL: /* write */ - hex2mem (buffer, page, length); + } + case __PDB_SET: /* write */ + { + memcpy (page, buffer, length); bytes = length; break; + } default: /* unknown */ - printk ("error: unknown RW flag: %d\n", rw); + { + printk ("pdb error: unknown RW flag: %d\n", rw); return 0; } + } unmap_domain_mem((void *)page); exit1: @@ -1061,9 +1259,146 @@ exit2: return bytes; } + +/***********************************************************************/ +/***********************************************************************/ + +/* BREAKPOINTS */ + +int +pdb_set_breakpoint (pdb_bwcpoint_p bwc) +{ + pdb_read_memory (bwc->address, 1, &bwc->original, &bwc->context); + pdb_write_memory (bwc->address, 1, &pdb_x86_bkpt, &bwc->context); + + pdb_bwc_list_add (bwc); + + return 0; +} + +int +pdb_clear_breakpoint (unsigned long address, int length, pdb_context_p ctx) +{ + int error = 0; + pdb_bwcpoint_p bwc = pdb_bwc_list_search (address, 1, &pdb_ctx); + + if (bwc == 0) + { + error = 3; /* breakpoint not found */ + } + + pdb_write_memory (address, 1, &bwc->original, &pdb_ctx); + + pdb_bwc_list_remove (bwc); + + return error; +} + +/***********************************************************************/ /***********************************************************************/ -void breakpoint(void); +/* WATCHPOINTS */ + +int pdb_process_watchpoint (pdb_bwcpoint_p bwc, pdb_generic_action action); + +int +pdb_set_watchpoint (pdb_bwcpoint_p bwc) +{ + return pdb_process_watchpoint (bwc, __PDB_SET); +} + +int +pdb_clear_watchpoint (pdb_bwcpoint_p bwc) +{ + return pdb_process_watchpoint (bwc, __PDB_CLEAR); +} + +/* set or clear watchpoint */ +int +pdb_process_watchpoint (pdb_bwcpoint_p bwc, pdb_generic_action action) +{ + int return_value; + pdb_invoke_args_t args; + + args.context = &bwc->context; + args.address = bwc->address; + args.length = bwc->length; + args.data = bwc; + switch (bwc->type) + { + case PDB_WP_WRITE : + { + args.action = (action == __PDB_SET) ? PDB_BWC_PAGE_WRITE_SET + : PDB_BWC_PAGE_WRITE_CLEAR; + break; + } + case PDB_WP_READ : + { + args.action = (action == __PDB_SET) ? PDB_BWC_PAGE_READ_SET + : PDB_BWC_PAGE_READ_CLEAR; + break; + } + case PDB_WP_ACCESS : + { + args.action = (action == __PDB_SET) ? PDB_BWC_PAGE_ACCESS_SET + : PDB_BWC_PAGE_ACCESS_CLEAR; + break; + } + default : + { + printk ("pdb error: incorrect watchpoint type [%d][%s]", + bwc->type, pdb_bwcpoint_type_s[bwc->type]); + break; + } + } + + return_value = pdb_invoke (pdb_bwc_page, &args); + + if (return_value < 0) + { + strcpy (pdb_out_buffer, "E03"); + } + + return return_value; +} + +/* + * set or clear watchpoint for a single page + */ + +int +pdb_bwc_page (int action, unsigned long addr, int length, + pdb_context_p ctx, int offset, void *data) +{ + int rval = 0; + + printk ("bwc: %s [0x%08lx:%x] 0x%x (0x%p)\n", + pdb_bwc_page_action_s[action], addr, length, offset, data); + + switch (action) + { + case PDB_BWC_PAGE_ACCESS_SET : + case PDB_BWC_PAGE_ACCESS_CLEAR : + case PDB_BWC_PAGE_WRITE_SET : + case PDB_BWC_PAGE_WRITE_CLEAR : + case PDB_BWC_PAGE_READ_SET : + case PDB_BWC_PAGE_READ_CLEAR : + { + printk ("fill in the blank [%s:%d]\n", __FILE__, __LINE__); + break; + } + default : + { + printk ("pdb error: unknown bwc page action [%d]\n", action); + break; + } + } + + return rval; +} + +/***********************************************************************/ +/***********************************************************************/ /* send the packet in buffer. */ void pdb_put_packet (unsigned char *buffer, int ack) @@ -1114,7 +1449,7 @@ void pdb_get_packet(char *buffer) count = 0; checksum = 0; - while (count < BUFMAX) + while (count < PDB_BUFMAX) { ch = pdb_get_char(); if (ch == '#') break; @@ -1132,10 +1467,11 @@ void pdb_get_packet(char *buffer) if (xmitcsum == checksum) { pdb_put_char('+'); + +#ifdef GDB_50_SUPPORT if (buffer[2] == ':') - { - printk ("pdb: obsolete gdb packet (sequence ID)\n"); - } + { printk ("pdb: obsolete gdb packet (sequence ID)\n"); } +#endif } else { @@ -1157,22 +1493,34 @@ int pdb_handle_exception(int exceptionVector, struct pt_regs *xen_regs) { int signal = 0; - struct pdb_breakpoint* bkpt; + struct pdb_bwcpoint* bkpt; int watchdog_save; unsigned long cr3; __asm__ __volatile__ ("movl %%cr3,%0" : "=r" (cr3) : ); +PDBTRC(4,printk("pdb handle exception\n")); +PDBTRC(4,printk(" cr3: 0x%lx\n", cr3)); +PDBTRC(4,printk(" eip: 0x%lx\n", xen_regs->eip)); +PDBTRC(4,printk(" except vector: 0x%x\n", exceptionVector)); +PDBTRC(4,printk(" xcs: 0x%x\n", xen_regs->xcs)); +PDBTRC(4,printk(" sys call next addr: 0x%lx\n", pdb_system_call_next_addr)); +PDBTRC(4,printk(" stepping: 0x%x\n", pdb_stepping)); +PDBTRC(4,printk(" system_call: 0x%x\n", pdb_system_call)); + /* If the exception is an int3 from user space then pdb is only interested if it re-wrote an instruction set the breakpoint. This occurs when leaving a system call from a domain. */ - if ( exceptionVector == 3 && + bkpt = pdb_bwcpoint_search(cr3, xen_regs->eip - 1); + if ( bkpt == NULL && + exceptionVector == 3 && (xen_regs->xcs & 3) == 3 && xen_regs->eip != pdb_system_call_next_addr + 1) { - TRC(printf("pdb: user bkpt (0x%x) at 0x%x:0x%lx:0x%lx\n", - exceptionVector, xen_regs->xcs & 3, cr3, xen_regs->eip)); + PDBTRC(1,printk("pdb: user bkpt (0x%x) at 0x%x:0x%lx:0x%lx 0x%lx\n", + exceptionVector, xen_regs->xcs & 3, cr3, + xen_regs->eip, pdb_system_call_next_addr)); return 1; } @@ -1182,7 +1530,6 @@ int pdb_handle_exception(int exceptionVector, * the user didn't press the magic debug key, * then we don't handle the exception. */ - bkpt = pdb_bkpt_search(cr3, xen_regs->eip - 1); if ( (bkpt == NULL) && !pdb_stepping && !pdb_system_call && @@ -1190,8 +1537,8 @@ int pdb_handle_exception(int exceptionVector, (exceptionVector != KEYPRESS_EXCEPTION) && xen_regs->eip < 0xc0000000) /* Linux-specific for now! */ { - TRC(printf("pdb: user bkpt (0x%x) at 0x%lx:0x%lx\n", - exceptionVector, cr3, xen_regs->eip)); + PDBTRC(1,printk("pdb: user bkpt (0x%x) at 0x%lx:0x%lx\n", + exceptionVector, cr3, xen_regs->eip)); return 1; } @@ -1222,22 +1569,23 @@ int pdb_handle_exception(int exceptionVector, } } - if ( exceptionVector == BREAKPT_EXCEPTION && bkpt != NULL) - { - /* Executed Int3: replace breakpoint byte with real program byte. */ - xen_regs->eip--; - } - /* returning to user space after a system call */ if ( xen_regs->eip == pdb_system_call_next_addr + 1) { - u_char instr[2]; /* REALLY REALLY REALLY STUPID */ - - mem2hex (&pdb_system_call_leave_instr, instr, sizeof(instr)); - - pdb_linux_set_values (instr, 1, pdb_system_call_next_addr, - pdb_ctx.process, pdb_ctx.ptbr); - + printk("BUG ******** \n"); + printk("BUG return to user space bug\n"); + printk("BUG ******** \n"); + + /* + * BUG: remember to delete the breakpoint!!! + * + */ + + /* this is always in a process context */ + pdb_write_memory (pdb_system_call_next_addr, + sizeof(pdb_system_call_leave_instr), + &pdb_system_call_leave_instr, &pdb_ctx); + pdb_system_call_next_addr = 0; pdb_system_call_leave_instr = 0; @@ -1253,6 +1601,13 @@ int pdb_handle_exception(int exceptionVector, } } + + if ( exceptionVector == BREAKPT_EXCEPTION && bkpt != NULL) + { + /* Executed Int3: replace breakpoint byte with real program byte. */ + xen_regs->eip--; + } + /* Generate a signal for GDB. */ switch ( exceptionVector ) { @@ -1303,9 +1658,6 @@ void initialize_pdb() { extern char opt_pdb[]; - /* Certain state must be initialised even when PDB will not be used. */ - memset((void *) &breakpoints, 0, sizeof(breakpoints)); - INIT_LIST_HEAD(&breakpoints.list); pdb_stepping = 0; if ( strcmp(opt_pdb, "none") == 0 ) @@ -1335,8 +1687,6 @@ void initialize_pdb() pdb_initialized = 1; } -void breakpoint(void) -{ - if ( pdb_initialized ) - asm("int $3"); -} +/***********************************************************************/ +/***********************************************************************/ + diff --git a/xen/common/debug-linux.c b/xen/common/debug-linux.c index 557997be3f..92135ceecc 100644 --- a/xen/common/debug-linux.c +++ b/xen/common/debug-linux.c @@ -45,7 +45,7 @@ static inline unsigned long machine_to_phys(unsigned long cr3, unsigned long machine) { unsigned long phys; - pdb_get_values((u_char *) &phys, sizeof(phys), cr3, + pdb_read_page((u_char *) &phys, sizeof(phys), cr3, (unsigned long) machine_to_phys_mapping + (machine >> PAGE_SHIFT) * 4); phys = (phys << PAGE_SHIFT) | (machine & ~PAGE_MASK); @@ -75,20 +75,20 @@ unsigned long pdb_linux_pid_task_struct (unsigned long cr3, int pid) unsigned long task_struct_pid; /* find the task_struct of the given process */ - pdb_get_values((u_char *) &task_struct_p, sizeof(task_struct_p), + pdb_read_page((u_char *) &task_struct_p, sizeof(task_struct_p), cr3, pdb_pidhash_addr + pid_hashfn(pid) * 4); /* find the correct task struct */ while (task_struct_p != (unsigned long)NULL) { - pdb_get_values((u_char *) &task_struct_pid, sizeof(task_struct_pid), + pdb_read_page((u_char *) &task_struct_pid, sizeof(task_struct_pid), cr3, task_struct_p + task_struct_pid_offset); if (task_struct_pid == pid) { break; } - pdb_get_values((u_char *) &task_struct_p, sizeof(task_struct_p), + pdb_read_page((u_char *) &task_struct_p, sizeof(task_struct_p), cr3, task_struct_p + task_struct_pidhash_next_offset); } if (task_struct_p == (unsigned long) NULL) @@ -116,96 +116,59 @@ unsigned long pdb_linux_pid_ptbr (unsigned long cr3, int pid) } /* get the mm_struct within the task_struct */ - pdb_get_values((u_char *) &mm_p, sizeof(mm_p), + pdb_read_page((u_char *) &mm_p, sizeof(mm_p), cr3, task_struct_p + task_struct_mm_offset); /* get the page global directory (cr3) within the mm_struct */ - pdb_get_values((u_char *) &pgd, sizeof(pgd), + pdb_read_page((u_char *) &pgd, sizeof(pgd), cr3, mm_p + mm_struct_pgd_offset); return pgd; } +/* read / write values from one page */ -/* read a byte from a process - * - * in: pid: process id - * cr3: ptbr for the process' domain - * addr: address to read - */ - -u_char pdb_linux_get_value(int pid, unsigned long cr3, unsigned long addr) -{ - u_char result = 0; - unsigned long pgd; - unsigned long l2tab, page; - - /* get the process' pgd */ - pgd = pdb_linux_pid_ptbr(cr3, pid); - - /* get the l2 table entry */ - pdb_get_values((u_char *) &l2tab, sizeof(l2tab), - cr3, pgd + (addr >> PGDIR_SHIFT) * 4); - l2tab = (unsigned long)__va(machine_to_phys(cr3, l2tab) & PAGE_MASK); - - /* get the page table entry */ - pdb_get_values((u_char *) &page, sizeof(page), - cr3, l2tab + ((addr & L1_PAGE_BITS) >> PAGE_SHIFT) * 4); - page = (unsigned long)__va(machine_to_phys(cr3, page) & PAGE_MASK); - - /* get the byte */ - pdb_get_values((u_char *) &result, sizeof(result), - cr3, page + (addr & ~PAGE_MASK)); - - return result; -} - -void pdb_linux_get_values(char *buffer, int length, unsigned long address, - int pid, unsigned long cr3) -{ - int loop; - - /* yes, this can be optimized... a lot */ - for (loop = 0; loop < length; loop++) - { - buffer[loop] = pdb_linux_get_value(pid, cr3, address + loop); - } -} - - -void pdb_linux_set_value(int pid, unsigned long cr3, unsigned long addr, - u_char *value) +u_char +pdb_linux_visit_page(int pid, unsigned long cr3, unsigned long addr, + int length, unsigned char *buffer, int action) { + u_char result = 0; unsigned long pgd; unsigned long l2tab, page; - + /* get the process' pgd */ pgd = pdb_linux_pid_ptbr(cr3, pid); - + /* get the l2 table entry */ - pdb_get_values((u_char *) &l2tab, sizeof(l2tab), - cr3, pgd + (addr >> PGDIR_SHIFT) * 4); + pdb_read_page((u_char *) &l2tab, sizeof(l2tab), + cr3, pgd + (addr >> PGDIR_SHIFT) * 4); l2tab = (unsigned long)__va(machine_to_phys(cr3, l2tab) & PAGE_MASK); - + /* get the page table entry */ - pdb_get_values((u_char *) &page, sizeof(page), - cr3, l2tab + ((addr & L1_PAGE_BITS) >> PAGE_SHIFT) * 4); + pdb_read_page((u_char *) &page, sizeof(page), + cr3, l2tab + ((addr & L1_PAGE_BITS) >> PAGE_SHIFT) * 4); page = (unsigned long)__va(machine_to_phys(cr3, page) & PAGE_MASK); - - /* set the byte */ - pdb_set_values(value, sizeof(u_char), cr3, page + (addr & ~PAGE_MASK)); -} - -void pdb_linux_set_values(char *buffer, int length, unsigned long address, - int pid, unsigned long cr3) -{ - int loop; - - /* it's difficult to imagine a more inefficient algorithm */ - for (loop = 0; loop < length; loop++) + + switch (action) { - pdb_linux_set_value(pid, cr3, address + loop, &buffer[loop * 2]); + case __PDB_GET : + { + pdb_read_page(buffer, length, cr3, page + (addr & ~PAGE_MASK)); + break; + } + case __PDB_SET : + { + pdb_write_page(buffer, length, cr3, page + (addr & ~PAGE_MASK)); + break; } + default : + { + printk ("pdb error: linux_visit_page unknown action [%d]\n", action); + break; + } + } + + return result; } /**********************************************************************/ @@ -232,18 +195,18 @@ int pdb_linux_process_list (unsigned long cr3, int array[], int max) int count = 0; /* task_p = init_task->next_task */ - pdb_get_values((u_char *) &task_p, sizeof(task_p), + pdb_read_page((u_char *) &task_p, sizeof(task_p), cr3, pdb_init_task_union_addr + task_struct_next_task_offset); while (task_p != pdb_init_task_union_addr) { - pdb_get_values((u_char *) &pid, sizeof(pid), + pdb_read_page((u_char *) &pid, sizeof(pid), cr3, task_p + task_struct_pid_offset); array[count % max] = pid; count++; - pdb_get_values((u_char *) &next_p, sizeof(next_p), + pdb_read_page((u_char *) &next_p, sizeof(next_p), cr3, task_p + task_struct_next_task_offset); task_p = next_p; } @@ -260,7 +223,7 @@ void pdb_linux_process_details (unsigned long cr3, int pid, char *buffer) task_struct_p = pdb_linux_pid_task_struct(cr3, pid); - pdb_get_values((u_char *) buffer, task_struct_comm_length, + pdb_read_page((u_char *) buffer, task_struct_comm_length, cr3, task_struct_p + task_struct_comm_offset); return; } diff --git a/xen/common/debug.c b/xen/common/debug.c index b17748a864..9ea4746934 100644 --- a/xen/common/debug.c +++ b/xen/common/debug.c @@ -65,7 +65,7 @@ void pdb_do_debug (dom0_op_t *op) case 'r' : { int loop; - u_char x; + u_char x = 0; unsigned long cr3; struct domain *d; @@ -81,8 +81,8 @@ void pdb_do_debug (dom0_op_t *op) { printk ("\n%08x ", op->u.debug.in1 + loop); } - x = pdb_linux_get_value(op->u.debug.in3, - cr3, op->u.debug.in1 + loop); + /* x = pdb_linux_get_value(op->u.debug.in3, + cr3, op->u.debug.in1 + loop); */ printk (" %02x", x); } printk ("\n"); diff --git a/xen/include/asm-x86/pdb.h b/xen/include/asm-x86/pdb.h index 2ed6a9a318..d33d5570fb 100644 --- a/xen/include/asm-x86/pdb.h +++ b/xen/include/asm-x86/pdb.h @@ -26,11 +26,30 @@ extern int pdb_page_fault; extern void initialize_pdb(void); -/* Get/set values from generic debug interface. */ -extern int pdb_set_values(u_char *buffer, int length, - unsigned long cr3, unsigned long addr); -extern int pdb_get_values(u_char *buffer, int length, - unsigned long cr3, unsigned long addr); +/* + * pdb debug context + */ +typedef struct pdb_context +{ + int valid; + int domain; + int process; + int system_call; /* 0x01 break on enter, 0x02 break on exit */ + unsigned long ptbr; +} pdb_context_t, *pdb_context_p; + +extern pdb_context_t pdb_ctx; + +/* read / write memory */ +extern int pdb_read_memory (unsigned long addr, int length, + unsigned char *data, pdb_context_p ctx); +extern int pdb_write_memory (unsigned long addr, int length, + unsigned char *data, pdb_context_p ctx); + +extern int pdb_read_page (u_char *buffer, int length, + unsigned long cr3, unsigned long addr); +extern int pdb_write_page (u_char *buffer, int length, + unsigned long cr3, unsigned long addr); /* External entry points. */ extern int pdb_handle_exception(int exceptionVector, @@ -38,29 +57,47 @@ extern int pdb_handle_exception(int exceptionVector, extern int pdb_serial_input(u_char c, struct pt_regs *regs); extern void pdb_do_debug(dom0_op_t *op); -/* PDB Context. */ -struct pdb_context +typedef enum pdb_generic_action { - int valid; - int domain; - int process; - int system_call; /* 0x01 break on enter, 0x02 break on exit */ - unsigned long ptbr; -}; -extern struct pdb_context pdb_ctx; + __PDB_GET, + __PDB_SET, + __PDB_CLEAR +} pdb_generic_action; -/* Breakpoints. */ -struct pdb_breakpoint +/* + * breakpoint, watchpoint, & catchpoint + * note: numbers must match GDB remote serial protocol Z command numbers + */ +enum pdb_bwcpoint_type { - struct list_head list; - unsigned long address; - unsigned long cr3; - domid_t domain; + PDB_BP_SOFTWARE = 0, + PDB_BP_HARDWARE = 1, + PDB_WP_WRITE = 2, + PDB_WP_READ = 3, + PDB_WP_ACCESS = 4 }; -extern void pdb_bkpt_add (unsigned long cr3, unsigned long address); -extern struct pdb_breakpoint* pdb_bkpt_search (unsigned long cr3, - unsigned long address); -extern int pdb_bkpt_remove (unsigned long cr3, unsigned long address); + +typedef struct pdb_bwcpoint +{ + struct list_head list; + unsigned long address; + int length; + enum pdb_bwcpoint_type type; /* how implemented */ + enum pdb_bwcpoint_type user_type; /* what was requested */ + pdb_context_t context; + + /* original value for breakpoint, one byte for x86 */ + unsigned char original; +} pdb_bwcpoint_t, *pdb_bwcpoint_p; + +void pdb_bwc_list_add (pdb_bwcpoint_p bwc); +void pdb_bwc_list_remove (pdb_bwcpoint_p bwc); +pdb_bwcpoint_p pdb_bwcpoint_search (unsigned long cr3, unsigned long address); + +int pdb_set_breakpoint (pdb_bwcpoint_p bwc); +int pdb_clear_breakpoint (unsigned long address, int length, + pdb_context_p ctx); + /* Conversions. */ extern int hex (char); @@ -85,4 +122,10 @@ void pdb_linux_syscall_enter_bkpt (struct pt_regs *regs, long error_code, void pdb_linux_syscall_exit_bkpt (struct pt_regs *regs, struct pdb_context *pdb_ctx); +/* tracing */ +extern int pdb_trace; +#define PDBTRC(_lvl_, _blahblah_) if (_lvl_ & pdb_trace) {_blahblah_;} +#define PDBTRC2(_lvl_, _blahblah_) \ + if (_lvl_ & pdb_trace) {printk("[%s:%d]",__FILE__,__LINE__); _blahblah_;} + #endif /* __PDB_H__ */ -- 2.30.2